package com.gy.zpchat;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Application;
import android.text.TextUtils;
import android.widget.EditText;

import com.google.gson.Gson;

import org.json.JSONObject;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import im.zego.zim.ZIM;
import im.zego.zim.callback.ZIMCallAcceptanceSentCallback;
import im.zego.zim.callback.ZIMCallCancelSentCallback;
import im.zego.zim.callback.ZIMCallEndSentCallback;
import im.zego.zim.callback.ZIMCallInvitationListQueriedCallback;
import im.zego.zim.callback.ZIMCallInvitationSentCallback;
import im.zego.zim.callback.ZIMCallQuitSentCallback;
import im.zego.zim.callback.ZIMCallRejectionSentCallback;
import im.zego.zim.callback.ZIMCallingInvitationSentCallback;
import im.zego.zim.callback.ZIMCombineMessageDetailQueriedCallback;
import im.zego.zim.callback.ZIMConversationDeletedCallback;
import im.zego.zim.callback.ZIMConversationDraftSetCallback;
import im.zego.zim.callback.ZIMConversationListQueriedCallback;
import im.zego.zim.callback.ZIMConversationMessageReceiptReadSentCallback;
import im.zego.zim.callback.ZIMConversationNotificationStatusSetCallback;
import im.zego.zim.callback.ZIMConversationPinnedListQueriedCallback;
import im.zego.zim.callback.ZIMConversationPinnedStateUpdatedCallback;
import im.zego.zim.callback.ZIMConversationUnreadMessageCountClearedCallback;
import im.zego.zim.callback.ZIMEventHandler;
import im.zego.zim.callback.ZIMGroupAttributesOperatedCallback;
import im.zego.zim.callback.ZIMGroupAttributesQueriedCallback;
import im.zego.zim.callback.ZIMGroupAvatarUrlUpdatedCallback;
import im.zego.zim.callback.ZIMGroupCreatedCallback;
import im.zego.zim.callback.ZIMGroupDismissedCallback;
import im.zego.zim.callback.ZIMGroupInfoQueriedCallback;
import im.zego.zim.callback.ZIMGroupJoinedCallback;
import im.zego.zim.callback.ZIMGroupLeftCallback;
import im.zego.zim.callback.ZIMGroupListQueriedCallback;
import im.zego.zim.callback.ZIMGroupMemberCountQueriedCallback;
import im.zego.zim.callback.ZIMGroupMemberInfoQueriedCallback;
import im.zego.zim.callback.ZIMGroupMemberKickedCallback;
import im.zego.zim.callback.ZIMGroupMemberListQueriedCallback;
import im.zego.zim.callback.ZIMGroupMemberNicknameUpdatedCallback;
import im.zego.zim.callback.ZIMGroupMemberRoleUpdatedCallback;
import im.zego.zim.callback.ZIMGroupMessageReceiptMemberListQueriedCallback;
import im.zego.zim.callback.ZIMGroupNameUpdatedCallback;
import im.zego.zim.callback.ZIMGroupNoticeUpdatedCallback;
import im.zego.zim.callback.ZIMGroupOwnerTransferredCallback;
import im.zego.zim.callback.ZIMGroupUsersInvitedCallback;
import im.zego.zim.callback.ZIMLogUploadedCallback;
import im.zego.zim.callback.ZIMLoggedInCallback;
import im.zego.zim.callback.ZIMMediaDownloadedCallback;
import im.zego.zim.callback.ZIMMediaMessageSentCallback;
import im.zego.zim.callback.ZIMMessageDeletedCallback;
import im.zego.zim.callback.ZIMMessageInsertedCallback;
import im.zego.zim.callback.ZIMMessageQueriedCallback;
import im.zego.zim.callback.ZIMMessageReactionAddedCallback;
import im.zego.zim.callback.ZIMMessageReactionDeletedCallback;
import im.zego.zim.callback.ZIMMessageReactionUserListQueriedCallback;
import im.zego.zim.callback.ZIMMessageReceiptsInfoQueriedCallback;
import im.zego.zim.callback.ZIMMessageReceiptsReadSentCallback;
import im.zego.zim.callback.ZIMMessageRevokedCallback;
import im.zego.zim.callback.ZIMMessageSentCallback;
import im.zego.zim.callback.ZIMRoomAttributesOperatedCallback;
import im.zego.zim.callback.ZIMRoomAttributesQueriedCallback;
import im.zego.zim.callback.ZIMRoomCreatedCallback;
import im.zego.zim.callback.ZIMRoomJoinedCallback;
import im.zego.zim.callback.ZIMRoomLeftCallback;
import im.zego.zim.callback.ZIMRoomMemberQueriedCallback;
import im.zego.zim.callback.ZIMRoomOnlineMemberCountQueriedCallback;
import im.zego.zim.callback.ZIMUserAvatarUrlUpdatedCallback;
import im.zego.zim.callback.ZIMUserNameUpdatedCallback;
import im.zego.zim.callback.ZIMUsersInfoQueriedCallback;
import im.zego.zim.entity.ZIMAppConfig;
import im.zego.zim.entity.ZIMCallAcceptConfig;
import im.zego.zim.entity.ZIMCallCancelConfig;
import im.zego.zim.entity.ZIMCallEndConfig;
import im.zego.zim.entity.ZIMCallEndedSentInfo;
import im.zego.zim.entity.ZIMCallInfo;
import im.zego.zim.entity.ZIMCallInvitationAcceptedInfo;
import im.zego.zim.entity.ZIMCallInvitationCancelledInfo;
import im.zego.zim.entity.ZIMCallInvitationEndedInfo;
import im.zego.zim.entity.ZIMCallInvitationQueryConfig;
import im.zego.zim.entity.ZIMCallInvitationReceivedInfo;
import im.zego.zim.entity.ZIMCallInvitationRejectedInfo;
import im.zego.zim.entity.ZIMCallInvitationSentInfo;
import im.zego.zim.entity.ZIMCallInvitationTimeoutInfo;
import im.zego.zim.entity.ZIMCallInviteConfig;
import im.zego.zim.entity.ZIMCallQuitConfig;
import im.zego.zim.entity.ZIMCallQuitSentInfo;
import im.zego.zim.entity.ZIMCallRejectConfig;
import im.zego.zim.entity.ZIMCallUserStateChangeInfo;
import im.zego.zim.entity.ZIMCallingInvitationSentInfo;
import im.zego.zim.entity.ZIMCallingInviteConfig;
import im.zego.zim.entity.ZIMCombineMessage;
import im.zego.zim.entity.ZIMConversation;
import im.zego.zim.entity.ZIMConversationChangeInfo;
import im.zego.zim.entity.ZIMConversationDeleteConfig;
import im.zego.zim.entity.ZIMConversationQueryConfig;
import im.zego.zim.entity.ZIMError;
import im.zego.zim.entity.ZIMErrorUserInfo;
import im.zego.zim.entity.ZIMFriendApplicationInfo;
import im.zego.zim.entity.ZIMFriendInfo;
import im.zego.zim.entity.ZIMGroup;
import im.zego.zim.entity.ZIMGroupAdvancedConfig;
import im.zego.zim.entity.ZIMGroupAttributesUpdateInfo;
import im.zego.zim.entity.ZIMGroupFullInfo;
import im.zego.zim.entity.ZIMGroupInfo;
import im.zego.zim.entity.ZIMGroupMemberInfo;
import im.zego.zim.entity.ZIMGroupMemberQueryConfig;
import im.zego.zim.entity.ZIMGroupMessageReceiptMemberQueryConfig;
import im.zego.zim.entity.ZIMGroupOperatedInfo;
import im.zego.zim.entity.ZIMLogConfig;
import im.zego.zim.entity.ZIMMediaMessage;
import im.zego.zim.entity.ZIMMessage;
import im.zego.zim.entity.ZIMMessageDeleteConfig;
import im.zego.zim.entity.ZIMMessageDeletedInfo;
import im.zego.zim.entity.ZIMMessageQueryConfig;
import im.zego.zim.entity.ZIMMessageReaction;
import im.zego.zim.entity.ZIMMessageReactionUserInfo;
import im.zego.zim.entity.ZIMMessageReactionUserQueryConfig;
import im.zego.zim.entity.ZIMMessageReceiptInfo;
import im.zego.zim.entity.ZIMMessageRevokeConfig;
import im.zego.zim.entity.ZIMMessageSendConfig;
import im.zego.zim.entity.ZIMMessageSentStatusChangeInfo;
import im.zego.zim.entity.ZIMRevokeMessage;
import im.zego.zim.entity.ZIMRoomAttributesSetConfig;
import im.zego.zim.entity.ZIMRoomAttributesUpdateInfo;
import im.zego.zim.entity.ZIMRoomInfo;
import im.zego.zim.entity.ZIMRoomMemberQueryConfig;
import im.zego.zim.entity.ZIMSystemMessage;
import im.zego.zim.entity.ZIMTextMessage;
import im.zego.zim.entity.ZIMUserFullInfo;
import im.zego.zim.entity.ZIMUserInfo;
import im.zego.zim.entity.ZIMUsersInfoQueryConfig;
import im.zego.zim.enums.ZIMBlacklistChangeAction;
import im.zego.zim.enums.ZIMConnectionEvent;
import im.zego.zim.enums.ZIMConnectionState;
import im.zego.zim.enums.ZIMConversationNotificationStatus;
import im.zego.zim.enums.ZIMConversationType;
import im.zego.zim.enums.ZIMErrorCode;
import im.zego.zim.enums.ZIMFriendApplicationListChangeAction;
import im.zego.zim.enums.ZIMFriendListChangeAction;
import im.zego.zim.enums.ZIMGroupEvent;
import im.zego.zim.enums.ZIMGroupMemberEvent;
import im.zego.zim.enums.ZIMGroupMemberState;
import im.zego.zim.enums.ZIMGroupState;
import im.zego.zim.enums.ZIMMediaFileType;
import im.zego.zim.enums.ZIMRoomEvent;
import im.zego.zim.enums.ZIMRoomState;
import com.gy.zpchat.ui.meeting.CallManager;
import com.gy.zpchat.util.AppLogger;
import com.gy.zpchat.util.debug.log.FloatingView;

import im.zego.zpns.ZPNsManager;

public class SDKManager {

    private static SDKManager sdkManager;
    private String TAG = "SDKManager";
    public Application context;

    public ZIMUserInfo zimUserInfo = new ZIMUserInfo();

    private Set<OnRoomStateCallback> onRoomStateChangedList = new HashSet<>();
    private Set<OnReceiveRoomMessage> onReceiveRoomMessageList = new HashSet<>();
    private Set<OnReceivePeerMessage> onReceivePeerMessageList = new HashSet<>();
    private Set<OnReceiveGroupMessage> onReceiveGroupMessageList = new HashSet<>();
    private Set<OnGroupNameUpdateCallback> onGroupNameUpdateCallbacks = new HashSet<>();

    private OnLoginStateCallback loginStateCallback;
    private OnRevokedReceivedCallback revokedReceivedCallback;
    private OnMessageSentStatusChangedCallback messageSentStatusChangedCallback;
    private OnRoomMemberLeftCallback memberLeftCallback;
    private OnRoomMemberJoinCallback memberJoinCallback;
    private OnRoomOnlineMemberCountCallback roomOnlineMemberCountCallback;
    private OnTokenCallback tokenCallback;
    private OnConversationChangedCallback conversationChangedCallback;
    private OnConversationTotalUnreadMessageCountUpdatedCallback conversationTotalUnreadMessageCountUpdatedCallback;

    private OnGroupStateChangedCallback groupStateChangedCallback;
    private OnGroupNoticeUpdatedCallback groupNoticeUpdatedCallback;
    private OnGroupAttributesUpdatedCallback groupAttributesUpdatedCallback;
    private OnGroupMemberStateChangedCallback groupMemberStateChangedCallback;
    private OnGroupMemberInfoUpdatedCallback groupMemberInfoUpdatedCallback;

    private OnCallInvitationTimeoutCallBack callInvitationTimeoutCallBack;
    private OnCallInvitationAcceptedCallback callInvitationAcceptedCallback;
    private OnCallInvitationCancelledCallback callInvitationCancelledCallback;
    private OnCallInvitationReceivedCallback callInvitationReceivedCallback;
    private OnCallInvitationRejectedCallback callInvitationRejectedCallback;
    private OnCallInvitationEndedCallBack callInvitationEndedCallBack;
    private OnCallInviteesAnsweredTimeoutCallback callInviteesAnsweredTimeoutCallback;
    private OnConversationMessageReceiptChangedCallback conversationMessageReceiptChangedCallback;
    private OnMessageReceiptChangedCallback messageReceiptChangedCallback;
    private OnCallUserStateChangedCallBack callUserStateChangedCallBack;

    private OnFriendEventCallBack friendEventCallBackCallBack;

    private OnFriendApplicationEventCallBack friendApplicationEventCallBackCallBack;

    private OnMessageReactionChangedCallback messageReactionChangedCallback;
    private OnMessageDeletedCallback messageDeletedCallback;
    private ZIM zim;
    private String extendedData = "extendedData";

    public static SDKManager sharedInstance() {
        if (sdkManager == null) {
            synchronized (SDKManager.class) {
                if (sdkManager == null) {
                    sdkManager = new SDKManager();
                }
            }
        }
        return sdkManager;
    }

    public ZIM getZim() {
        return zim;
    }

    // MARK: - Main
    public void create(Application application, String appSign) {
        this.context = application;
        ZIMLogConfig zimLogConfig = new ZIMLogConfig();
        zimLogConfig.logSize = 5 * 1024 * 1024;
        ZIM.setLogConfig(zimLogConfig);
        if (TextUtils.isEmpty(appSign)) {
            AppLogger.getInstance().d("create ZIM by appID: %s", KeyCenter.appID);
            zim = ZIM.create(KeyCenter.appID, application);
        } else {
            AppLogger.getInstance().d("create ZIM by appID :%s, appSign :%s", KeyCenter.appID, appSign);
            ZIMAppConfig appConfig = new ZIMAppConfig();
            appConfig.appID = KeyCenter.appID;
            appConfig.appSign = appSign;
            zim = ZIM.create(appConfig, application);
        }
        zim.setEventHandler(new ZIMEventHandler() {
            @Override
            public void onUserInfoUpdated(ZIM zim, ZIMUserFullInfo info) {
                super.onUserInfoUpdated(zim, info);
                AppLogger.getInstance().d("[%s] [onUserInfoUpdated] [info:%s]", TAG, info);
            }

            @Override
            public void onMessageSentStatusChanged(ZIM zim, ArrayList<ZIMMessageSentStatusChangeInfo> messageSentStatusChangeInfoList) {
                super.onMessageSentStatusChanged(zim, messageSentStatusChangeInfoList);
                AppLogger.getInstance().d("[%s] [onMessageSentStatusChanged] [messageList:%s]", TAG, messageSentStatusChangeInfoList.toString());
                if (messageSentStatusChangedCallback != null) {
                    messageSentStatusChangedCallback.onMessageSentStatusChanged(zim, messageSentStatusChangeInfoList);
                }
            }
//
            @Override
            public void onMessageDeleted(ZIM zim, ZIMMessageDeletedInfo deletedInfo) {
                super.onMessageDeleted(zim, deletedInfo);
                AppLogger.getInstance().d("[%s] [onMessageDeleted] [deletedInfo:%s]", TAG, deletedInfo.toString());
                if (messageDeletedCallback!=null){
                    messageDeletedCallback.onMessageDeleted(zim,deletedInfo);
                }
            }

            @Override
            public void onMessageReceiptChanged(ZIM zim, ArrayList<ZIMMessageReceiptInfo> infos) {
                super.onMessageReceiptChanged(zim, infos);
                AppLogger.getInstance().d("[%s] [onMessageReceiptChanged] [infos:%s]", TAG, infos.toString());
                if (messageReceiptChangedCallback != null) {
                    messageReceiptChangedCallback.onMessageReceiptChanged(zim, infos);
                }
            }

            @Override
            public void onFriendApplicationListChanged(ZIM zim,
                                                       ArrayList<ZIMFriendApplicationInfo> friendApplicationInfoList, ZIMFriendApplicationListChangeAction action) {

                AppLogger.getInstance().d(
                    "onFriendApplicationReceived friendApplicationInfoList: %s",
                    new Gson().toJson(friendApplicationInfoList));
                if(action == ZIMFriendApplicationListChangeAction.ADDED) {
                    if (friendApplicationEventCallBackCallBack != null) {
                        friendApplicationEventCallBackCallBack.onFriendApplicationListAdded(
                                zim, friendApplicationInfoList);
                    }
                }
            }

            @Override
            public void onFriendApplicationUpdated(
                ZIM zim, ArrayList<ZIMFriendApplicationInfo> friendApplicationInfoList) {
                AppLogger.getInstance().d("onFriendApplicationUpdated friendInfoList: %s",
                                          new Gson().toJson(friendApplicationInfoList));
                if (friendApplicationEventCallBackCallBack != null) {
                    friendApplicationEventCallBackCallBack.onFriendApplicationInfoUpdated(
                        zim, friendApplicationInfoList);
                }
            }

            @Override
            public void onBlacklistChanged(ZIM zim,
                                           ArrayList<ZIMUserInfo> userList,  ZIMBlacklistChangeAction action) {}

            @Override
            public void onFriendInfoUpdated(ZIM zim, ArrayList<ZIMFriendInfo> friendInfoList) {
                AppLogger.getInstance().d("onFriendInfoUpdated friendInfoList: %s",
                                          new Gson().toJson(friendInfoList));
                if (friendEventCallBackCallBack != null) {
                    friendEventCallBackCallBack.onFriendInfoUpdated(zim, friendInfoList);
                }
            }

            @Override
            public void onFriendListChanged(ZIM zim,
                                            ArrayList<ZIMFriendInfo> friendInfoList, ZIMFriendListChangeAction action) {

                AppLogger.getInstance().d("onFriendListChanged action :%s friendInfoList: %s",
                                          action.name(), new Gson().toJson(friendInfoList));
                if (friendEventCallBackCallBack != null) {
                    if (action == ZIMFriendListChangeAction.ADDED) {
                        friendEventCallBackCallBack.onFriendListAdded(zim, friendInfoList);
                    } else {
                        friendEventCallBackCallBack.onFriendListDeleted(zim, friendInfoList);
                    }
                }
            }

            @Override
            public void onConversationMessageReceiptChanged(
                ZIM zim, ArrayList<ZIMMessageReceiptInfo> infos) {
                super.onConversationMessageReceiptChanged(zim, infos);
                AppLogger.getInstance().d("[%s] [onConversationMessageReceiptChanged] [infos:%s]", TAG, infos.toString());
                if (conversationMessageReceiptChangedCallback != null) {
                    conversationMessageReceiptChangedCallback.onConversationMessageReceiptChanged(zim, infos);
                }
            }

            @Override
            public void onMessageRevokeReceived(ZIM zim, ArrayList<ZIMRevokeMessage> messageList) {
                super.onMessageRevokeReceived(zim, messageList);
                AppLogger.getInstance().d("[%s] [onMessageRevokeReceived] [messageList:%s]", TAG, messageList.toString());
                if (revokedReceivedCallback != null) {
                    revokedReceivedCallback.onMessageRevokeReceived(zim, messageList);
                }
            }

            @Override
            public void onMessageReactionsChanged(ZIM zim, ArrayList<ZIMMessageReaction> infos) {
                super.onMessageReactionsChanged(zim, infos);
                AppLogger.getInstance().d("[%s] [onMessageReactionChanged] [infos %s]", TAG,
                        infos.toString());
                if (messageReactionChangedCallback != null) {
                    messageReactionChangedCallback.onMessageReactionChanged(zim, infos);
                }
            }

            @Override
            public void onConnectionStateChanged(ZIM zim, ZIMConnectionState state, ZIMConnectionEvent event, JSONObject extendedData) {
                super.onConnectionStateChanged(zim, state, event, extendedData);
                AppLogger.getInstance().d("[%s] [onConnectionStateChanged] [state:%s, event:%s][extendedData:%s]", TAG, state.name(), event.name(),extendedData.toString());
                if (loginStateCallback != null) {
                    loginStateCallback.onConnectionStateChanged(zim, state, event, extendedData);
                }
            }

            @Override
            public void onGroupAvatarUrlUpdated(ZIM zim, String groupAvatarUrl, ZIMGroupOperatedInfo operatedInfo, String groupID) {
                super.onGroupAvatarUrlUpdated(zim, groupAvatarUrl, operatedInfo, groupID);
                AppLogger.getInstance().d("[%s] [onGroupAvatarUrlUpdated] [groupAvatarUrl:%s][operatedInfo:%s][groupID:%s]", TAG, groupAvatarUrl,operatedInfo,groupID);

            }

            @Override
            public void onError(ZIM zim, ZIMError errorInfo) {
                super.onError(zim, errorInfo);
                AppLogger.getInstance().d("[%s] [onError] [error]", TAG, errorInfo.toString());
            }

            @Override
            public void onTokenWillExpire(ZIM zim, int second) {
                super.onTokenWillExpire(zim, second);
                AppLogger.getInstance().d("[%s] [onTokenWillExpire] [second:%d]", TAG, second);
                if (tokenCallback != null) {
                    tokenCallback.OnTokenWillExpire(zim, second);
                } else {
                    if (MyApplication.getCurrentActivity() != null) {
                        EditText editText = new EditText(MyApplication.getCurrentActivity());
                        editText.setHint("input the new token");
                        new AlertDialog.Builder(MyApplication.getCurrentActivity())
                                .setTitle("TokenWillExpire")
                                .setView(editText)
                                .setPositiveButton("ok", (dialog, which) -> {
                                    String newToken = editText.getText().toString();
                                    renewToken(newToken);
                                    dialog.dismiss();
                                })
                                .setNegativeButton("cancel", (dialog, which) -> dialog.dismiss())
                                .show();
                    }
                }

            }

            @Override
            public void onConversationChanged(ZIM zim, ArrayList<ZIMConversationChangeInfo> conversationChangeInfoList) {
                super.onConversationChanged(zim, conversationChangeInfoList);
                AppLogger.getInstance().d("[%s] [onConversationChanged] [info:%s]", TAG, conversationChangeInfoList.toString());

                if (conversationChangedCallback != null) {
                    conversationChangedCallback.onConversationChanged(zim, conversationChangeInfoList);
                }
            }

            @Override
            public void onConversationTotalUnreadMessageCountUpdated(ZIM zim, int totalUnreadMessageCount) {
                super.onConversationTotalUnreadMessageCountUpdated(zim, totalUnreadMessageCount);
                AppLogger.getInstance().d("[%s] [onConversationTotalUnreadMessageCountUpdated] [count:%s]", TAG, totalUnreadMessageCount);
                if (conversationTotalUnreadMessageCountUpdatedCallback != null) {
                    conversationTotalUnreadMessageCountUpdatedCallback.onConversationTotalUnreadMessageCountUpdated(zim, totalUnreadMessageCount);
                }
                ZPNsManager.getInstance().setApplicationIconBadgeNumber(MyApplication.context,totalUnreadMessageCount);
            }

            @Override
            public void onReceivePeerMessage(ZIM zim, ArrayList<ZIMMessage> messageList, String fromUserID) {
                super.onReceivePeerMessage(zim, messageList, fromUserID);
                AppLogger.getInstance().d("[%s] [onReceivePeerMessage] [messageList:%s][fromUserID:%s]", TAG, messageList.toString(),fromUserID);
                for (final OnReceivePeerMessage callback : onReceivePeerMessageList) {
                    if (callback != null) {
                        callback.onReceivePeerMessage(zim, messageList, fromUserID);
                    }
                }
            }

            @Override
            public void onReceiveGroupMessage(ZIM zim, ArrayList<ZIMMessage> messageList, String fromGroupID) {
                super.onReceiveGroupMessage(zim, messageList, fromGroupID);
                AppLogger.getInstance().d("[%s] [onReceiveGroupMessage] [messageList:%s][fromGroupID:%s]", TAG, messageList.toString(),fromGroupID);

                for (final OnReceiveGroupMessage callback : onReceiveGroupMessageList) {
                    if (callback != null) {
                        callback.onReceiveGroupMessage(zim, messageList, fromGroupID);
                    }
                }
            }

            @Override
            public void onReceiveRoomMessage(ZIM zim, ArrayList<ZIMMessage> messageList, String fromRoomID) {
                super.onReceiveRoomMessage(zim, messageList, fromRoomID);
                for (final OnReceiveRoomMessage callback : onReceiveRoomMessageList) {
                    if (callback != null) {
                        callback.onReceiveRoomMessage(zim, messageList, fromRoomID);
                    }
                }
                AppLogger.getInstance().d("[%s] [onReceiveRoomMessage] [messageList:%s][fromRoomID:%s]", TAG, messageList.toString(),fromRoomID);

            }

            @Override
            public void onRoomMemberJoined(ZIM zim, ArrayList<ZIMUserInfo> memberList, String roomID) {
                super.onRoomMemberJoined(zim, memberList, roomID);
                AppLogger.getInstance().d("[%s] [onRoomMemberJoined] [memberList:%s][roomID:%s]", TAG, memberList.toString(),roomID);

                if (memberJoinCallback != null) {
                    memberJoinCallback.onRoomMemberJoinCallback(zim, memberList, roomID);
                }
                queryRoomOnlineMemberCount(roomID, new ZIMRoomOnlineMemberCountQueriedCallback() {
                    @Override
                    public void onRoomOnlineMemberCountQueried(String roomID, int count, ZIMError errorInfo) {

                        AppLogger.getInstance().d("[%s] [onRoomOnlineMemberCountQueried] [roomID:%s, count:%s ]", TAG, roomID, count);
                        if (roomOnlineMemberCountCallback != null) {
                            roomOnlineMemberCountCallback.onRoomOnlineMemberCount(zim, count);
                        }
                    }
                });
            }

            @Override
            public void onRoomMemberLeft(ZIM zim, ArrayList<ZIMUserInfo> memberList, String roomID) {
                super.onRoomMemberLeft(zim, memberList, roomID);
                AppLogger.getInstance().d("[%s] [onRoomMemberLeft] [memberList:%s, roomID:%s]", TAG, memberList, roomID);

                if (memberLeftCallback != null) {
                    memberLeftCallback.onRoomMemberLeft(zim, memberList, roomID);
                }
                queryRoomOnlineMemberCount(roomID, new ZIMRoomOnlineMemberCountQueriedCallback() {
                    @Override
                    public void onRoomOnlineMemberCountQueried(String roomID, int count, ZIMError errorInfo) {

                        AppLogger.getInstance().d("[%s] [onRoomOnlineMemberCountQueried] [roomID:%s, count:%s ]", TAG, roomID, count);
                        if (roomOnlineMemberCountCallback != null) {
                            roomOnlineMemberCountCallback.onRoomOnlineMemberCount(zim, count);
                        }
                    }
                });
            }

            @Override
            public void onRoomStateChanged(ZIM zim, ZIMRoomState state, ZIMRoomEvent event, JSONObject extendedData, String roomID) {
                super.onRoomStateChanged(zim, state, event, extendedData, roomID);
                AppLogger.getInstance().d("[%s] [onRoomStateChanged] [state:%s, event:%s,extendedData:%s,roomID:%s]", TAG, state, event,extendedData,roomID);
                for (final OnRoomStateCallback callback : onRoomStateChangedList) {
                    if (callback != null) {
                        callback.onRoomStateChanged(zim, state, event, extendedData);
                    }
                }
            }

            @Override
            public void onRoomAttributesUpdated(ZIM zim, ZIMRoomAttributesUpdateInfo info, String roomID) {
                super.onRoomAttributesUpdated(zim, info, roomID);
                AppLogger.getInstance().d("[%s] [onRoomAttributesUpdated] [info:%s, roomID:%s]", TAG, info, roomID);
            }

            @Override
            public void onRoomAttributesBatchUpdated(ZIM zim, ArrayList<ZIMRoomAttributesUpdateInfo> infos, String roomID) {
                super.onRoomAttributesBatchUpdated(zim, infos, roomID);
                AppLogger.getInstance().d("[%s] [onRoomAttributesBatchUpdated] [info:%s, roomID:%s]", TAG, infos, roomID);
            }

            @Override
            public void onGroupStateChanged(ZIM zim, ZIMGroupState state, ZIMGroupEvent event, ZIMGroupOperatedInfo operatedInfo, ZIMGroupFullInfo groupInfo) {
                super.onGroupStateChanged(zim, state, event, operatedInfo, groupInfo);
                AppLogger.getInstance().d("[%s] [onGroupStateChanged] [state:%s, event:%s,operatedInfo:%s,groupInfo:%s]", TAG, state, event,operatedInfo.toString(),groupInfo.toString());
                if (groupStateChangedCallback != null) {
                    groupStateChangedCallback.onGroupStateChanged(zim, state, event, operatedInfo, groupInfo);
                }
            }

            @Override
            public void onGroupNameUpdated(ZIM zim, String groupName, ZIMGroupOperatedInfo operatedInfo, String groupID) {
                super.onGroupNameUpdated(zim, groupName, operatedInfo, groupID);
                AppLogger.getInstance().d("[%s] [onGroupNameUpdated] [groupName:%s,operatedInfo:%s,groupID:%s]", TAG, groupName,operatedInfo, groupID);
                for (OnGroupNameUpdateCallback groupNameUpdateCallback : onGroupNameUpdateCallbacks) {
                    groupNameUpdateCallback.onGroupNameUpdated(zim, groupName, operatedInfo, groupID);
                }
            }

            @Override
            public void onGroupNoticeUpdated(ZIM zim, String groupNotice, ZIMGroupOperatedInfo operatedInfo, String groupID) {
                super.onGroupNoticeUpdated(zim, groupNotice, operatedInfo, groupID);
                AppLogger.getInstance().d("[%s] [onGroupNoticeUpdated] [groupNotice:%s,operatedInfo:%s,groupID:%s]", TAG, groupNotice,operatedInfo, groupID);
                if (groupNoticeUpdatedCallback != null) {
                    groupNoticeUpdatedCallback.onGroupNoticeUpdated(zim, groupNotice, operatedInfo, groupID);
                }
            }

            @Override
            public void onGroupMemberStateChanged(ZIM zim, ZIMGroupMemberState state, ZIMGroupMemberEvent event, ArrayList<ZIMGroupMemberInfo> userList, ZIMGroupOperatedInfo operatedInfo, String groupID) {
                super.onGroupMemberStateChanged(zim, state, event, userList, operatedInfo, groupID);
                AppLogger.getInstance().d("[%s] [onGroupMemberStateChanged] [state:%s, event:%s, userList:%s,operatedInfo:%s,groupID:%s]", TAG, state, event,userList,operatedInfo, groupID);
                if (groupMemberStateChangedCallback != null) {
                    groupMemberStateChangedCallback.onGroupMemberStateChanged(zim, userList, state, event, operatedInfo, groupID);
                }

            }

            @Override
            public void onGroupMemberInfoUpdated(ZIM zim, ArrayList<ZIMGroupMemberInfo> userList, ZIMGroupOperatedInfo operatedInfo, String groupID) {
                super.onGroupMemberInfoUpdated(zim, userList, operatedInfo, groupID);
                AppLogger.getInstance().d("[%s] [onGroupMemberInfoUpdated] [userList:%s,operatedInfo:%s,groupID:%s]", TAG,userList,operatedInfo, groupID);
                if (groupMemberInfoUpdatedCallback != null) {
                    groupMemberInfoUpdatedCallback.onGroupMemberInfoUpdated(zim, userList, operatedInfo, groupID);
                }
            }

            @Override
            public void onGroupAttributesUpdated(ZIM zim, ArrayList<ZIMGroupAttributesUpdateInfo> infos, ZIMGroupOperatedInfo operatedInfo, String groupID) {
                super.onGroupAttributesUpdated(zim, infos, operatedInfo, groupID);
                AppLogger.getInstance().d("[%s] [onGroupAttributesUpdated] [infos:%s,operatedInfo:%s,groupID:%s]", TAG,infos,operatedInfo, groupID);
                if (groupAttributesUpdatedCallback != null) {
                    groupAttributesUpdatedCallback.onGroupAttributesUpdated(zim, infos, operatedInfo, groupID);
                }
            }


            @Override
            public void onCallInvitationTimeout(ZIM zim, ZIMCallInvitationTimeoutInfo info, String callID) {
                super.onCallInvitationTimeout(zim, info, callID);
                AppLogger.getInstance().d("[%s] [onCallInvitationTimeout] [info:%s],[callID:%s]", TAG, info,callID);
                if (callInvitationTimeoutCallBack != null) {
                    callInvitationTimeoutCallBack.onCallInvitationTimeout(zim,info,callID);
                }
            }

            @Override
            public void onCallInvitationAccepted(ZIM zim, ZIMCallInvitationAcceptedInfo info, String callID) {
                super.onCallInvitationAccepted(zim, info, callID);
                AppLogger.getInstance().d("[%s] [onCallInvitationAccepted] [callID:%s],[info:%s]", TAG, callID, info.toString());
                if (callInvitationAcceptedCallback != null) {
                    callInvitationAcceptedCallback.onCallInvitationAccepted(zim, info, callID);
                }
            }

            @Override
            public void onCallInvitationCancelled(ZIM zim, ZIMCallInvitationCancelledInfo info, String callID) {
                super.onCallInvitationCancelled(zim, info, callID);
                AppLogger.getInstance().d("[%s] [onCallInvitationCancelled] [callID:%s],[info:%s]", TAG, callID, info.toString());
                if (callInvitationCancelledCallback != null) {
                    callInvitationCancelledCallback.onCallInvitationCancelled(zim, info, callID);
                }
            }

            @Override
            public void onCallInvitationReceived(ZIM zim, ZIMCallInvitationReceivedInfo info, String callID) {
                super.onCallInvitationReceived(zim, info, callID);
                AppLogger.getInstance().d("[%s] [onCallInvitationReceived] [callID:%s],[info:%s]", TAG, callID, info.toString());
                if (callInvitationReceivedCallback != null) {
                    callInvitationReceivedCallback.onCallInvitationReceived(zim, info, callID);
                }
            }

            @Override
            public void onCallInvitationRejected(ZIM zim, ZIMCallInvitationRejectedInfo info, String callID) {
                super.onCallInvitationRejected(zim, info, callID);
                AppLogger.getInstance().d("[%s] [onCallInvitationRejected] [callID:%s],[info:%s]", TAG, callID, info.toString());
                if (callInvitationRejectedCallback != null) {
                    callInvitationRejectedCallback.onCallInvitationRejected(zim, info, callID);
                }
            }

            @Override
            public void onCallInviteesAnsweredTimeout(ZIM zim, ArrayList<String> invitees, String callID) {
                super.onCallInviteesAnsweredTimeout(zim, invitees, callID);
                AppLogger.getInstance().d("[%s] [onCallInviteesAnsweredTimeout] [callID:%s],[invitees:%s]", TAG, callID, invitees.toString());
                if (callInviteesAnsweredTimeoutCallback != null) {
                    callInviteesAnsweredTimeoutCallback.onCallInviteesAnsweredTimeout(zim, invitees, callID);
                }
            }

            @Override
            public void onCallInvitationEnded(ZIM zim, ZIMCallInvitationEndedInfo info, String callID) {
                super.onCallInvitationEnded(zim, info, callID);
                AppLogger.getInstance().d("[%s] [onCallInvitationEnded] [callID:%s],[info:%s]", TAG, callID, info.toString());
                if (callInvitationEndedCallBack !=null){
                    callInvitationEndedCallBack.onCallInvitationEnded(zim,info,callID);
                }
            }

            @Override
            public void onCallUserStateChanged(ZIM zim, ZIMCallUserStateChangeInfo info, String callID) {
                super.onCallUserStateChanged(zim, info, callID);
                AppLogger.getInstance().d("[%s] [onCallUserStateChanged] [callID:%s],[info:%s]", TAG, callID, info.toString());
                if (callUserStateChangedCallBack!=null){
                    callUserStateChangedCallBack.onCallUserStateChanged(zim,info,callID);
                }
            }
        });
    }

    public void queryUsersInfo(List<String> userIDList,boolean isQueryFromSever, ZIMUsersInfoQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [login] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryUsersInfo] [userIDList:%s]", TAG, userIDList.toString());
        ZIMUsersInfoQueryConfig config = new ZIMUsersInfoQueryConfig();
        config.isQueryFromServer = isQueryFromSever;
        zim.queryUsersInfo(userIDList, config, new ZIMUsersInfoQueriedCallback() {
            @Override
            public void onUsersInfoQueried(ArrayList<ZIMUserFullInfo> userList, ArrayList<ZIMErrorUserInfo> errorUserList, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onUsersInfoQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onUsersInfoQueried(userList, errorUserList, errorInfo);
                }
            }
        });
    }

    private void renewToken(String newToken) {
        zim.renewToken(newToken, (token, errorInfo) ->
                AppLogger.getInstance().d("[%s] [onTokenRenewed] [zimErrorCode:%s]", TAG, errorInfo.code.name())
        );
    }

    public void upload() {
        zim.uploadLog(new ZIMLogUploadedCallback() {
            @Override
            public void onLogUploaded(ZIMError zimErrorCode) {
                AppLogger.getInstance().d("[%s] [onLogUploaded] [errorCode:%s]", TAG, zimErrorCode.code);
            }
        });
    }

    // Login, you must log in before using all functions.
    public void login(String userID, String userName, ZIMLoggedInCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [login] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [login] [userID:%s token:%s],[enableAppSign:%s]", TAG, userID, KeyCenter.token, MyApplication.enableAppSign);
        zimUserInfo.userID = userID;
        zimUserInfo.userName = userName;
        zim.login(zimUserInfo, MyApplication.enableAppSign ? "" : KeyCenter.token, errorInfo -> {
            if (errorInfo.code == ZIMErrorCode.SUCCESS) {
                MyApplication.sUserId = userID;
            }
            AppLogger.getInstance().d("[%s] [onLoggedIn] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
            if (callback != null) {
                callback.onLoggedIn(errorInfo);
            }
        });
    }

    // Log out of ZIM service.
    public void logout() {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [logout] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [logout]", TAG);
        MyApplication.sUserId = "";
        KeyCenter.access_token = "";
        CallManager.sharedInstance().clear();
        zim.logout();
        ZPNsManager.getInstance().unregisterPush();
    }

    // MARK: - Conversation
    public void queryConversationList(ZIMConversation conversation, int count, ZIMConversationListQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryConversationList] [zim = null]", TAG);
            return;
        }
        ZIMConversationQueryConfig config = new ZIMConversationQueryConfig();
        config.nextConversation = conversation;
        config.count = count;

        AppLogger.getInstance().d("[%s] [queryConversationList] [count:%d]", TAG, count);
        zim.queryConversationList(config, new ZIMConversationListQueriedCallback() {
            @Override
            public void onConversationListQueried(ArrayList<ZIMConversation> conversationList, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onConversationListQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onConversationListQueried(conversationList, errorInfo);
                }
            }
        });
    }

    public void queryConversationPinnedList(ZIMConversation conversation, int count, ZIMConversationPinnedListQueriedCallback callback){
        if (zim == null){
            AppLogger.getInstance().d("[%s] [queryConversationPinnedList] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryConversationPinnedList] [count:%d]", TAG, count);
        ZIMConversationQueryConfig config = new ZIMConversationQueryConfig();
        config.nextConversation = conversation;
        config.count = count;

        zim.queryConversationPinnedList(config, new ZIMConversationPinnedListQueriedCallback() {
            @Override
            public void onConversationPinnedListQueried(ArrayList<ZIMConversation> conversationList, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onConversationPinnedListQueried] [errorCode:%s, message:%s,size:%d]", TAG, errorInfo.code, errorInfo.message,conversationList.size());
                if (callback != null) {
                    callback.onConversationPinnedListQueried(conversationList, errorInfo);
                }
            }
        });
    }

    public void updateConversationPinned(boolean isPinned, ZIMConversation conversation, ZIMConversationPinnedStateUpdatedCallback callback){
        if (zim == null){
            AppLogger.getInstance().d("[%s] [updateConversationPinned] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [updateConversationPinned] [conversationID:%s]", TAG, conversation.conversationID);
        zim.updateConversationPinnedState(isPinned, conversation.conversationID, conversation.type, new ZIMConversationPinnedStateUpdatedCallback() {
            @Override
            public void onConversationPinnedStateUpdated(String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onConversationPinnedStateUpdated] [conversationID:%s,errorCode:%s, message:%s]", TAG, conversationID,errorInfo.code, errorInfo.message);
                if (callback !=null){
                    callback.onConversationPinnedStateUpdated(conversationID,conversationType,errorInfo);
                }
            }
        });
    }
    public void deleteConversation(ZIMConversation conversation, ZIMConversationDeletedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [deleteConversation] [zim = null]", TAG);
            return;
        }

        AppLogger.getInstance().d("[%s] [deleteConversation] [conversationID:%s]", TAG, conversation.conversationID);
        zim.deleteConversation(conversation.conversationID, conversation.type, new ZIMConversationDeleteConfig(), new ZIMConversationDeletedCallback() {
            @Override
            public void onConversationDeleted(String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onConversationDeleted] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onConversationDeleted(conversationID, conversationType, errorInfo);
                }
            }
        });
    }

    public void clearConversationUnreadMessageCount(String conversationID, ZIMConversationType type) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [clearConversationUnreadMessageCount] [zim = null]", TAG);
            return;
        }

        AppLogger.getInstance().d("[%s] [clearConversationUnreadMessageCount] [conversationID:%s]", TAG, conversationID);
        zim.clearConversationUnreadMessageCount(conversationID, type, new ZIMConversationUnreadMessageCountClearedCallback() {
            @Override
            public void onConversationUnreadMessageCountCleared(String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onConversationUnreadMessageCountCleared] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
            }
        });
    }

    // MARK: - Message
    public void sendMessageReceiptsRead(List<ZIMMessage> messageList, String conversationID, ZIMConversationType conversationType, ZIMMessageReceiptsReadSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [sendMessageReceiptsRead] [zim = null]", TAG);
            return;
        }

        AppLogger.getInstance().d("[%s] [sendMessageReceiptsRead] [conversationID:%s],[message size:%d]", TAG, conversationID, messageList.size());
        zim.sendMessageReceiptsRead(messageList, conversationID, conversationType, new ZIMMessageReceiptsReadSentCallback() {
            @Override
            public void onMessageReceiptsReadSent(String conversationID, ZIMConversationType conversationType, ArrayList<Long> errorMessageIDs, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onMessageReceiptsReadSent] [errorCode:%s, message:%s]", TAG, errorInfo.code.name(), errorInfo.message);
                if (callback != null) {
                    callback.onMessageReceiptsReadSent(conversationID, conversationType, errorMessageIDs, errorInfo);
                }
            }
        });
    }

    public void sendConversationMessageReceiptRead(String conversationID, ZIMConversationType conversationType, ZIMConversationMessageReceiptReadSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [sendConversationMessageReceiptRead] [zim = null]", TAG);
            return;
        }

        AppLogger.getInstance().d("[%s] [sendConversationMessageReceiptRead] [conversationID:%s]", TAG, conversationID);
        zim.sendConversationMessageReceiptRead(conversationID, conversationType, new ZIMConversationMessageReceiptReadSentCallback() {
            @Override
            public void onConversationMessageReceiptReadSent(String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onConversationMessageReceiptReadSent] [errorCode:%s, message:%s]", TAG, errorInfo.code.name(), errorInfo.message);
                if (callback != null) {
                    callback.onConversationMessageReceiptReadSent(conversationID, conversationType, errorInfo);
                }
            }
        });
    }

    public void addMessageReaction(String reactionType, ZIMMessage message,
                                   ZIMMessageReactionAddedCallback callback){
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [addMessageReaction] [zim = null]", TAG);
            return;
        }
        zim.addMessageReaction(reactionType, message, new ZIMMessageReactionAddedCallback() {
            @Override
            public void onMessageReactionAdded(ZIMMessageReaction reaction, ZIMError error) {
                AppLogger.getInstance().d("[%s] [onMessageReactionAdded] reaction:%s,errorCode:%s,errorMessage:%s", TAG,reaction.toString(),error.code,error.message);
                if (callback!=null){
                    callback.onMessageReactionAdded(reaction,error);
                }
            }
        });

    }

    public void deleteMessageReaction(String reactionType, ZIMMessage message,
                                      ZIMMessageReactionDeletedCallback callback){
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [deleteMessageReaction] [zim = null]", TAG);
            return;
        }
        zim.deleteMessageReaction(reactionType, message, new ZIMMessageReactionDeletedCallback() {
            @Override
            public void onMessageReactionDeleted(ZIMMessageReaction reaction, ZIMError error) {
                AppLogger.getInstance().d("[%s] [onMessageReactionDeleted] reaction:%s,errorCode:%s,errorMessage:%s", TAG,reaction.toString(),error.code,error.message);
                if (callback!=null){
                    callback.onMessageReactionDeleted(reaction,error);
                }
            }
        });

    }
    public void queryMessageReactionUserList(ZIMMessage message,
                                             ZIMMessageReactionUserQueryConfig config,
                                             ZIMMessageReactionUserListQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryReactionUserList] [zim = null]", TAG);
            return;
        }

        zim.queryMessageReactionUserList(message, config, new ZIMMessageReactionUserListQueriedCallback() {
            @Override
            public void onMessageReactionUserListQueried(ZIMMessage message, ArrayList<ZIMMessageReactionUserInfo> userList, String reactionType, long nextFlag, int totalCount, ZIMError error) {
                if (callback != null) {
                    AppLogger.getInstance().d(
                            "[%s] [onMessageReactionUsersQueried] userInfos:%s,nextFla:%d,totalCount%d,[errorCode:%s,message:%s]",
                            TAG, userList.toString(), nextFlag, totalCount, error.code, error.message);
                    callback.onMessageReactionUserListQueried(message,userList, reactionType, nextFlag,
                            totalCount, error);
                }
            }
        });
    }

    public void queryHistoryMessage(String conversationID,
                                    ZIMConversationType conversationType,
                                    ZIMMessageQueryConfig config,
                                    final ZIMMessageQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryHistoryMessage] [zim = null]", TAG);
            return;
        }

        AppLogger.getInstance().d("[%s] [queryHistoryMessage] [conversationID:%s]", TAG, conversationID);
        zim.queryHistoryMessage(conversationID, conversationType, config, new ZIMMessageQueriedCallback() {
            @Override
            public void onMessageQueried(String conversationID, ZIMConversationType conversationType, ArrayList<ZIMMessage> messageList, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onMessageQueried] [errorCode:%s, message:%s],message list:%s", TAG, errorInfo.code.name(), errorInfo.message,messageList);
                if (callback != null) {
                    callback.onMessageQueried(conversationID, conversationType, messageList, errorInfo);
                }
            }
        });
    }


    public void sendMediaMessage(ZIMMediaMessage message, String toConversationID, ZIMConversationType conversationType, ZIMMessageSendConfig config, ZIMMediaMessageSentCallback callback) {
        message.extendedData = extendedData;
        zim.sendMediaMessage(message, toConversationID, conversationType, config, new ZIMMediaMessageSentCallback() {
            @Override
            public void onMessageAttached(ZIMMediaMessage message) {
                AppLogger.getInstance().d("onMessageAttached,extended data:%s", message.extendedData);
            }

            @Override
            public void onMediaUploadingProgress(ZIMMediaMessage message, long currentFileSize, long totalFileSize) {
                callback.onMediaUploadingProgress(message, currentFileSize, totalFileSize);
            }

            @Override
            public void onMessageSent(ZIMMediaMessage message, ZIMError errorInfo) {
                AppLogger.getInstance().d("onMessageSent,errorCode:%d,errorMsg:%s", errorInfo.code.value(), errorInfo.message);
                AppLogger.getInstance().d("onMessageSent,extended data:%s", message.extendedData);
                callback.onMessageSent(message, errorInfo);

            }
        });

    }

    public void sendPeerMessage(ZIMMessage message, String userID, ZIMMessageSendConfig sendConfig, ZIMMessageSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [sendMsg] [zim = null]", TAG);
            return;
        }
        if (message instanceof ZIMTextMessage) {
            AppLogger.getInstance().d("[%s] [sendPeerMessage] [userID:%s msg:%s]", TAG, userID, ((ZIMTextMessage) message).message);
        }
        message.extendedData = extendedData;
        zim.sendPeerMessage(message, userID, sendConfig, new ZIMMessageSentCallback() {

            @Override
            public void onMessageAttached(ZIMMessage message) {
                AppLogger.getInstance().d("onMessageAttached,extended data:%s", message.extendedData);
            }

            @Override
            public void onMessageSent(ZIMMessage message, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onMessageSent] [messageID:%s, errorCode:%s, message:%s]", TAG, message.getMessageID(), errorInfo.code.name(), errorInfo.message);
                AppLogger.getInstance().d("onMessageSent,extended data:%s", message.extendedData);

                if (callback != null) {
                    callback.onMessageSent(message, errorInfo);
                }
            }
        });
    }

    public void sendMessage(ZIMMessage message, String conversationID,ZIMConversationType type, ZIMMessageSendConfig sendConfig, ZIMMessageSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [sendMessage] [zim = null]", TAG);
            return;
        }

        message.extendedData = extendedData;
        zim.sendMessage(message, conversationID,type, sendConfig, new ZIMMessageSentCallback() {

            @Override
            public void onMessageAttached(ZIMMessage message) {
                AppLogger.getInstance().d("onMessageAttached,extended data:%s", message.extendedData);
            }

            @Override
            public void onMessageSent(ZIMMessage message, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onMessageSent] [messageID:%s, errorCode:%s, message:%s]", TAG, message.toString(), errorInfo.code.name(), errorInfo.message);

                if (callback != null) {
                    callback.onMessageSent(message, errorInfo);
                }
            }
        });
    }

    public void downloadMediaFile(ZIMMediaMessage message, ZIMMediaFileType type, ZIMMediaDownloadedCallback callback) {
        zim.downloadMediaFile(message, type, new ZIMMediaDownloadedCallback() {
            @Override
            public void onMediaDownloaded(ZIMMediaMessage message, ZIMError errorInfo) {
                callback.onMediaDownloaded(message, errorInfo);
            }

            @Override
            public void onMediaDownloadingProgress(ZIMMediaMessage message, long currentFileSize, long totalFileSize) {
                AppLogger.getInstance().d("[%s] [onMediaDownloadingProgress] [currentFileSize:%d, totalFileSize:%d, message: %s]",
                        TAG, currentFileSize, currentFileSize, message.getMessageID());
                callback.onMediaDownloadingProgress(message, currentFileSize, totalFileSize);
            }
        });
    }

    public void sendGroupMessage(ZIMMessage message, String groupID, ZIMMessageSendConfig sendConfig, ZIMMessageSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [sendGroupMessage] [zim == null]", TAG);
            return;
        }
        if (message instanceof ZIMTextMessage) {
            AppLogger.getInstance().d("[%s] [sendGroupMessage] [userID:%s msg:%s]", TAG, groupID, ((ZIMTextMessage) message).message);
        }
        message.extendedData = extendedData;
        zim.sendGroupMessage(message, groupID, sendConfig, new ZIMMessageSentCallback() {

            @Override
            public void onMessageAttached(ZIMMessage message) {
                AppLogger.getInstance().d("onMessageAttached,extended data:%s", message.extendedData);

            }

            @Override
            public void onMessageSent(ZIMMessage message, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onMessageSent] [messageID:%s, errorCode:%s, message:%s]", TAG, message.getMessageID(), errorInfo.code.name(), errorInfo.message);
                AppLogger.getInstance().d("onMessageSent,extended data:%s", message.extendedData);
                if (callback != null) {
                    callback.onMessageSent(message, errorInfo);
                }
            }
        });
    }

    public void sendRoomMessage(ZIMMessage message, String roomID, ZIMMessageSendConfig sendConfig, ZIMMessageSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [sendRoomMsg] [zim = null]", TAG);
            return;
        }
        if (message instanceof ZIMTextMessage) {
            AppLogger.getInstance().d("[%s] [sendRoomMsg] [userID:%s msg:%s]", TAG, roomID, ((ZIMTextMessage) message).message);
        }
        message.extendedData = extendedData;
        zim.sendRoomMessage(message, roomID, sendConfig, new ZIMMessageSentCallback() {

            @Override
            public void onMessageAttached(ZIMMessage message) {
                AppLogger.getInstance().d("onMessageAttached,extended data:%s", message.extendedData);

            }

            @Override
            public void onMessageSent(ZIMMessage message, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onMessageSent] [messageID:%s, errorCode:%s, message:%s]", TAG, message.getMessageID(), errorInfo.code.name(), errorInfo.message);
                AppLogger.getInstance().d("onMessageSent,extended data:%s", message.extendedData);

                if (callback != null) {
                    callback.onMessageSent(message, errorInfo);
                }
            }
        });
    }

    public void deleteMessage(String conversationID, ZIMConversationType type, List<ZIMMessage> messageList, ZIMMessageDeleteConfig config, ZIMMessageDeletedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [deleteMessage] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [deleteMessage] [conversationID:%s ,type:%s]", TAG, conversationID, type);
        zim.deleteMessages(messageList, conversationID, type, config, new ZIMMessageDeletedCallback() {
            @Override
            public void onMessageDeleted(String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onMessageDeleted] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onMessageDeleted(conversationID, conversationType, errorInfo);
                }
            }
        });
    }

    public void deleteAllMessage(String conversationID, ZIMConversationType type, ZIMMessageDeleteConfig config, ZIMMessageDeletedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [deleteMessageByConversationID] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [deleteMessageByConversationID] [conversationID:%s ,type:%s]", TAG, conversationID, type);
        zim.deleteAllMessage(conversationID, type, config, new ZIMMessageDeletedCallback() {
            @Override
            public void onMessageDeleted(String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onMessageDeleted] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onMessageDeleted(conversationID, conversationType, errorInfo);
                }
            }
        });
    }

    public void setConversationNotificationStatus(ZIMConversationNotificationStatus status, String conversationID, ZIMConversationType type, ZIMConversationNotificationStatusSetCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [setConversationNotificationStatus] [zim = null]", TAG);
        }
        AppLogger.getInstance().d("[%s] [setConversationNotificationStatus] [status:%s ,convID:%s, type:%s]", TAG, status, conversationID, type);
        zim.setConversationNotificationStatus(status, conversationID, type, new ZIMConversationNotificationStatusSetCallback() {
            @Override
            public void onConversationNotificationStatusSet(String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onConversationNotificationStatusSet] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onConversationNotificationStatusSet(conversationID, conversationType, errorInfo);
                }
            }
        });
    }

    // MARK: - Group
    public void createGroup(ZIMGroupInfo groupInfo, List<String> userIDList, ZIMGroupCreatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [createGroup] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [createGroup] [groupID:%s]", TAG, groupInfo.groupID);

        ZIMGroupAdvancedConfig advancedConfig = new ZIMGroupAdvancedConfig();
        HashMap<String, String> groupAttrMap = new HashMap();
        groupAttrMap.put("item", "game");
        groupAttrMap.put("admin", "owner");
        advancedConfig.groupAttributes = groupAttrMap;

        zim.createGroup(groupInfo, userIDList, advancedConfig, new ZIMGroupCreatedCallback() {
            @Override
            public void onGroupCreated(ZIMGroupFullInfo groupInfo, ArrayList<ZIMGroupMemberInfo> userList, ArrayList<ZIMErrorUserInfo> errorUserList, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onGroupCreated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupCreated(groupInfo, userList, errorUserList, errorInfo);
                }
            }
        });
    }

    public void dismissGroup(String groupID, ZIMGroupDismissedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [dismissGroup] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [dismissGroup] [groupID:%s]", TAG, groupID);
        zim.dismissGroup(groupID, new ZIMGroupDismissedCallback() {
            @Override
            public void onGroupDismissed(String groupID, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupDismissed] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupDismissed(groupID, errorInfo);
                }
            }
        });
    }

    public void joinGroup(String groupID, ZIMGroupJoinedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [joinGroup] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [joinGroup] [groupID:%s]", TAG, groupID);
        zim.joinGroup(groupID, new ZIMGroupJoinedCallback() {
            @Override
            public void onGroupJoined(ZIMGroupFullInfo groupInfo, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onGroupJoined] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupJoined(groupInfo, errorInfo);
                }
            }
        });
    }

    public void leaveGroup(String groupID, ZIMGroupLeftCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [leaveGroup] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [leaveGroup] [groupID:%s]", TAG, groupID);
        zim.leaveGroup(groupID, new ZIMGroupLeftCallback() {
            @Override
            public void onGroupLeft(String groupID, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupLeft] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupLeft(groupID, errorInfo);
                }
            }
        });
    }

    public void inviteUsersIntoGroup(List<String> userIDList, String groupID, ZIMGroupUsersInvitedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [inviteUsersIntoGroup] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [inviteUsersIntoGroup] [groupID:%s]", TAG, groupID);
        zim.inviteUsersIntoGroup(userIDList, groupID, new ZIMGroupUsersInvitedCallback() {
            @Override
            public void onGroupUsersInvited(String groupID, ArrayList<ZIMGroupMemberInfo> userList, ArrayList<ZIMErrorUserInfo> errorUserList, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupUsersInvited] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupUsersInvited(groupID, userList, errorUserList, errorInfo);
                }
            }
        });
    }

    public void kickGroupMembers(List<String> userIDList, String groupID, ZIMGroupMemberKickedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [kickGroupMembers] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [kickGroupMembers] [groupID:%s]", TAG, groupID);
        zim.kickGroupMembers(userIDList, groupID, new ZIMGroupMemberKickedCallback() {
            @Override
            public void onGroupMemberKicked(String groupID, ArrayList<String> kickedUserIDList, ArrayList<ZIMErrorUserInfo> errorUserList, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupMemberKicked] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupMemberKicked(groupID, kickedUserIDList, errorUserList, errorInfo);
                }
            }
        });
    }

    public void transferGroupOwner(String toUserID, String groupID, ZIMGroupOwnerTransferredCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [transferGroupOwner] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [transferGroupOwner] [groupID:%s, userID: %s]", TAG, groupID, toUserID);
        zim.transferGroupOwner(toUserID, groupID, new ZIMGroupOwnerTransferredCallback() {
            @Override
            public void onGroupOwnerTransferred(String groupID, String toUserID, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupOwnerTransferred] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupOwnerTransferred(groupID, toUserID, errorInfo);
                }
            }
        });
    }

    public void updateGroupName(String groupName, String groupID, ZIMGroupNameUpdatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [updateGroupName] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [updateGroupName] [groupID:%s, groupName:%s]", TAG, groupID, groupName);
        zim.updateGroupName(groupName, groupID, new ZIMGroupNameUpdatedCallback() {
            @Override
            public void onGroupNameUpdated(String groupID, String groupName, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupNameUpdated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupNameUpdated(groupID, groupName, errorInfo);
                }
            }
        });
    }

    public void updateUserAvatar(String userAvatar, ZIMUserAvatarUrlUpdatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [updateUserAvatar] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [updateUserAvatar] [userAvatar:%s]", TAG, userAvatar);
        zim.updateUserAvatarUrl(userAvatar, new ZIMUserAvatarUrlUpdatedCallback() {
            @Override
            public void onUserAvatarUrlUpdated(String userAvatarUrl, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onUserAvatarUrlUpdated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onUserAvatarUrlUpdated(userAvatarUrl, errorInfo);
                }
            }
        });
    }

    public void updateGroupAvatar(String groupId, String avatar, ZIMGroupAvatarUrlUpdatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [updateGroupAvatar] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [updateGroupAvatar] [userName:%s]", TAG, avatar);
        zim.updateGroupAvatarUrl(avatar, groupId, new ZIMGroupAvatarUrlUpdatedCallback() {

            @Override
            public void onGroupAvatarUrlUpdated(String groupID, String groupAvatarUrl, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onGroupAvatarUpdated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupAvatarUrlUpdated(groupID, groupAvatarUrl, errorInfo);
                }
            }
        });

    }

    public void updateUserName(String userName, ZIMUserNameUpdatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [updateUserName] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [updateUserName] [userName:%s]", TAG, userName);
        zim.updateUserName(userName, new ZIMUserNameUpdatedCallback() {

            @Override
            public void onUserNameUpdated(String userName, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onUserNameUpdated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onUserNameUpdated(userName, errorInfo);
                }
            }
        });
    }

    public void updateGroupNotice(String groupNotice, String groupID, ZIMGroupNoticeUpdatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [updateGroupNotice] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [updateGroupNotice] [groupID:%s, groupNotice:%s]", TAG, groupID, groupNotice);
        zim.updateGroupNotice(groupNotice, groupID, new ZIMGroupNoticeUpdatedCallback() {
            @Override
            public void onGroupNoticeUpdated(String groupID, String groupNotice, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupNoticeUpdated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupNoticeUpdated(groupID, groupNotice, errorInfo);
                }
            }
        });
    }

    public void queryGroupInfo(String groupID, ZIMGroupInfoQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryGroupInfo] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryGroupInfo] [groupID:%s]", TAG, groupID);
        zim.queryGroupInfo(groupID, new ZIMGroupInfoQueriedCallback() {
            @Override
            public void onGroupInfoQueried(ZIMGroupFullInfo groupInfo, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupInfoQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupInfoQueried(groupInfo, errorInfo);
                }
            }
        });
    }

    public void queryGroupMemberCount(String groupID, ZIMGroupMemberCountQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryGroupMemberCount] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryGroupMemberCount] [groupID:%s]", TAG, groupID);
        zim.queryGroupMemberCount(groupID, new ZIMGroupMemberCountQueriedCallback() {
            @Override
            public void onGroupMemberCountQueried(String groupID, int count, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onGroupMemberCountQueried] [errorCode:%s,message:%s, groupId:%s,count:%d]", TAG, errorInfo.code, errorInfo.message, groupID, count);
                if (callback != null) {
                    callback.onGroupMemberCountQueried(groupID, count, errorInfo);
                }
            }
        });
    }

    public void queryGroupAttributes(List<String> keys, String groupID, ZIMGroupAttributesQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryGroupAttributes] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryGroupAttributes] [groupID:%s, keys:%s]", TAG, groupID, keys.toString());
        zim.queryGroupAttributes(keys, groupID, new ZIMGroupAttributesQueriedCallback() {
            @Override
            public void onGroupAttributesQueried(String groupID, HashMap<String, String> groupAttributes, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupAttributesQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupAttributesQueried(groupID, groupAttributes, errorInfo);
                }
            }
        });
    }

    public void setGroupAttributes(HashMap<String, String> groupAttributes, String groupID, ZIMGroupAttributesOperatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [setGroupAttributes] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [setGroupAttributes] [groupID:%s, attr:%s]", TAG, groupID, groupAttributes.toString());
        zim.setGroupAttributes(groupAttributes, groupID, new ZIMGroupAttributesOperatedCallback() {
            @Override
            public void onGroupAttributesOperated(String groupID, ArrayList<String> errorKeys, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupAttributesOperated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupAttributesOperated(groupID, errorKeys, errorInfo);
                }
            }
        });
    }

    public void setGroupMemberRole(int role, String userID, String groupID, ZIMGroupMemberRoleUpdatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [setGroupMemberRole] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [setGroupMemberRole] [groupID:%s, userID:%s, role:%d]", TAG, groupID, userID, role);
        zim.setGroupMemberRole(role, userID, groupID, new ZIMGroupMemberRoleUpdatedCallback() {
            @Override
            public void onGroupMemberRoleUpdated(String groupID, String forUserID, int role, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupMemberRoleUpdated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupMemberRoleUpdated(groupID, forUserID, role, errorInfo);
                }
            }
        });
    }

    public void setGroupMemberNickname(String nickname, String userID, String groupID, ZIMGroupMemberNicknameUpdatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [setGroupMemberNickname] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [setGroupMemberNickname] [groupID:%s, userID:%s, nickname:%s]", TAG, groupID, userID, nickname);
        zim.setGroupMemberNickname(nickname, userID, groupID, new ZIMGroupMemberNicknameUpdatedCallback() {
            @Override
            public void onGroupMemberNicknameUpdated(String groupID, String forUserID, String nickname, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupMemberNicknameUpdated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupMemberNicknameUpdated(groupID, forUserID, nickname, errorInfo);
                }
            }
        });
    }

    public void queryGroupMemberInfo(String userID, String groupID, ZIMGroupMemberInfoQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryGroupMemberInfo] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryGroupMemberInfo] [groupID:%s, userID:%s]", TAG, groupID, userID);
        zim.queryGroupMemberInfo(userID, groupID, new ZIMGroupMemberInfoQueriedCallback() {
            @Override
            public void onGroupMemberInfoQueried(String groupID, ZIMGroupMemberInfo userInfo, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupMemberInfoQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupMemberInfoQueried(groupID, userInfo, errorInfo);
                }
            }
        });
    }

    public void queryGroupList(ZIMGroupListQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryGroupList] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryGroupList]", TAG);
        zim.queryGroupList(new ZIMGroupListQueriedCallback() {
            @Override
            public void onGroupListQueried(ArrayList<ZIMGroup> groupList, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupListQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupListQueried(groupList, errorInfo);
                }
            }
        });
    }

    public void queryGroupMemberList(String groupID, ZIMGroupMemberQueryConfig config, ZIMGroupMemberListQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryGroupMemberList] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryGroupMemberList] [groupID: %s]", TAG, groupID);
        zim.queryGroupMemberList(groupID, config, new ZIMGroupMemberListQueriedCallback() {
            @Override
            public void onGroupMemberListQueried(String groupID, ArrayList<ZIMGroupMemberInfo> userList, int nextFlag, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onGroupMemberListQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupMemberListQueried(groupID, userList, nextFlag, errorInfo);
                }
            }
        });
    }

    public void queryGroupMessageReceiptUnreadMemberList(ZIMMessage message, String groupID, ZIMGroupMessageReceiptMemberQueryConfig config, ZIMGroupMessageReceiptMemberListQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryGroupMessageReceiptUnreadMemberList] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryGroupMessageReceiptUnreadMemberList] [groupID: %s]", TAG, groupID);
        zim.queryGroupMessageReceiptUnreadMemberList(message, groupID, config, new ZIMGroupMessageReceiptMemberListQueriedCallback() {
            @Override
            public void onGroupMessageReceiptMemberListQueried(String groupID, ArrayList<ZIMGroupMemberInfo> userList, int nextFlag, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onGroupMessageReceiptUnReadMemberListQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupMessageReceiptMemberListQueried(groupID, userList, nextFlag, errorInfo);
                }
            }
        });
    }

    public void queryGroupMessageReceiptReadMemberList(ZIMMessage message, String groupID, ZIMGroupMessageReceiptMemberQueryConfig config, ZIMGroupMessageReceiptMemberListQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryGroupMessageReceiptReadMemberList] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryGroupMessageReceiptReadMemberList] [groupID: %s]", TAG, groupID);
        zim.queryGroupMessageReceiptReadMemberList(message, groupID, config, new ZIMGroupMessageReceiptMemberListQueriedCallback() {
            @Override
            public void onGroupMessageReceiptMemberListQueried(String groupID, ArrayList<ZIMGroupMemberInfo> userList, int nextFlag, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onGroupMessageReceiptReadMemberListQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onGroupMessageReceiptMemberListQueried(groupID, userList, nextFlag, errorInfo);
                }
            }
        });
    }

    // MARK: - Room
    public void createRoom(String roomID, String roomName, ZIMRoomCreatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [createRoom] [zim = null]", TAG);
            return;
        }
        ZIMRoomInfo zimRoomInfo = new ZIMRoomInfo();
        zimRoomInfo.roomID = roomID;
        zimRoomInfo.roomName = roomName;
        zim.createRoom(zimRoomInfo, callback);
        AppLogger.getInstance().d("[%s] [createRoom roomID:%s, roomName:%s]", TAG, roomID, roomName);
    }

    // Join a room
    public void joinRoom(String roomID, ZIMRoomJoinedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [joinRoom] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [joinRoom roomID:%s]", TAG, roomID);
        zim.joinRoom(roomID, callback);
    }

    // Leave a room
    public void leaveRoom(final String roomID) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [logoutRoom] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [logoutRoom] [roomID:%s]", TAG, roomID);
        zim.leaveRoom(roomID, new ZIMRoomLeftCallback() {
            @Override
            public void onRoomLeft(String roomID, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onRoomLeft] [roomID:%s]", TAG, roomID);
            }
        });
    }

    public void setRoomAttributes(HashMap<String, String> roomAttributes, String roomID, ZIMRoomAttributesSetConfig config, ZIMRoomAttributesOperatedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [setRoomAttributes] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [setRoomAttributes] [roomID:%s, attr:%s]", TAG, roomID, roomAttributes.toString());
        zim.setRoomAttributes(roomAttributes, roomID, config, new ZIMRoomAttributesOperatedCallback() {
            @Override
            public void onRoomAttributesOperated(String roomID, ArrayList<String> errorKeys, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onRoomAttributesOperated] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onRoomAttributesOperated(roomID, errorKeys, errorInfo);
                }
            }
        });
    }


    public void queryPeerMessage(String conversationID, ZIMMessage message, int count, boolean reverse, final ZIMMessageQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryPeerMessage] [zim = null]", TAG);
            return;
        }

        ZIMMessageQueryConfig queryConfig = new ZIMMessageQueryConfig();
        queryConfig.nextMessage = message;
        queryConfig.count = count;
        queryConfig.reverse = reverse;

        queryHistoryMessage(conversationID, ZIMConversationType.PEER, queryConfig, callback);
    }

    public void queryGroupMessage(String conversationID, ZIMMessage message, int count, boolean reverse, final ZIMMessageQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryGroupMessage] [zim = null]", TAG);
            return;
        }

        ZIMMessageQueryConfig queryConfig = new ZIMMessageQueryConfig();
        queryConfig.nextMessage = message;
        queryConfig.count = count;
        queryConfig.reverse = reverse;

        queryHistoryMessage(conversationID, ZIMConversationType.GROUP, queryConfig, callback);
    }

    public void queryRoomMessage(String conversationID, ZIMMessage message, int count, boolean reverse, final ZIMMessageQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryRoomMessage] [zim = null]", TAG);
            return;
        }

        ZIMMessageQueryConfig queryConfig = new ZIMMessageQueryConfig();
        queryConfig.nextMessage = message;
        queryConfig.count = count;
        queryConfig.reverse = reverse;

        queryHistoryMessage(conversationID, ZIMConversationType.ROOM, queryConfig, callback);
    }

    public void queryRoomMember(String roomID, ZIMRoomMemberQueryConfig config, ZIMRoomMemberQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryMember] [zim = null]", TAG);
            return;
        }
        zim.queryRoomMemberList(roomID, config, callback);
    }

    public void queryRoomOnlineMemberCount(String roomID, ZIMRoomOnlineMemberCountQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryRoomOnlineMemberCount] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryRoomOnlineMemberCount] [roomID:%s]", TAG, roomID);
        zim.queryRoomOnlineMemberCount(roomID, callback);
    }

    public void queryRoomAllAttributes(String roomID, ZIMRoomAttributesQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryRoomAllAttributes] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [queryRoomAllAttributes] [roomID:%s]", TAG, roomID);
        zim.queryRoomAllAttributes(roomID, new ZIMRoomAttributesQueriedCallback() {
            @Override
            public void onRoomAttributesQueried(String roomID, HashMap<String, String> roomAttributes, ZIMError errorInfo) {

                AppLogger.getInstance().d("[%s] [onAllAttributesQueried] [errorCode:%s, message:%s]", TAG, errorInfo.code, errorInfo.message);
                if (callback != null) {
                    callback.onRoomAttributesQueried(roomID, roomAttributes, errorInfo);
                }
            }
        });
    }

    public void insertSystemMessage(String content, ZIMConversationType type, String conversationId) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [insertSystemMessage] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [insertSystemMessage] [content:%s] [type] [conversationId]", TAG, content, type, conversationId);
        ZIMSystemMessage zimSystemMessage = new ZIMSystemMessage(content);
        zim.insertMessageToLocalDB(zimSystemMessage, conversationId, type, "system", new ZIMMessageInsertedCallback() {
            @Override
            public void onMessageInserted(ZIMMessage message, ZIMError errorInfo) {


                AppLogger.getInstance().d("[%s] [onMessageInserted][errorInfo.code] [message:%s] [type] [conversationId]", TAG, errorInfo.code, ((ZIMSystemMessage) message).message, type, conversationId);
            }
        });
    }

    public void callInvite(List<String> invitees, ZIMCallInviteConfig config, ZIMCallInvitationSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [callInvite] [zim = null]", TAG);
            return;
        }
        zim.callInvite(invitees, config, new ZIMCallInvitationSentCallback() {
            @Override
            public void onCallInvitationSent(String callID, ZIMCallInvitationSentInfo info, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onCallInvitationSent] callID:%s,info error size:%s,error cod :%d,error msg:%s", TAG, callID, info.errorInvitees.size(), errorInfo.code.value(), errorInfo.message);
                if (callback != null) {
                    callback.onCallInvitationSent(callID, info, errorInfo);
                }
            }
        });
    }

    public void callingInvite(String callID, List<String> invitees, ZIMCallingInviteConfig config, ZIMCallingInvitationSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [callingInvite] [zim = null]", TAG);
            return;
        }
        zim.callingInvite(invitees,callID,config, new ZIMCallingInvitationSentCallback() {
            @Override
            public void onCallingInvitationSent(String callID, ZIMCallingInvitationSentInfo info, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onCallingInvitationSent] callID:%s,info:%s,error cod :%d,error msg:%s", TAG, callID, info.toString(), errorInfo.code.value(), errorInfo.message);
                if (callback != null) {
                    callback.onCallingInvitationSent(callID, info, errorInfo);
                }
            }
        });
    }

    public void callEnd(String callID, ZIMCallEndConfig config, ZIMCallEndSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [callEnd] [zim = null]", TAG);
            return;
        }
        zim.callEnd(callID, config, new ZIMCallEndSentCallback() {

            @Override
            public void onCallEndSent(String callID, ZIMCallEndedSentInfo info, ZIMError errorInfo) {
                if (callback!=null){
                    callback.onCallEndSent(callID,info,errorInfo);
                }
            }
        });
    }

    public void callQuit(String callID, ZIMCallQuitConfig config, ZIMCallQuitSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [callQuit] [zim = null]", TAG);
            return;
        }
        zim.callQuit(callID, config, new ZIMCallQuitSentCallback() {
            @Override
            public void onCallQuitSent(String callID, ZIMCallQuitSentInfo info, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onCallQuitSent] callID:%s,info:%s,error code :%d,error msg:%s", TAG, callID,info.toString(),errorInfo.code.value(), errorInfo.message);
                if (callback != null) {
                    callback.onCallQuitSent(callID, info,errorInfo);
                }
            }
        });
    }

    public void queryCallList(ZIMCallInvitationQueryConfig config, ZIMCallInvitationListQueriedCallback callback){
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryCallList] [zim = null]", TAG);
            return;
        }
        zim.queryCallInvitationList(config, new ZIMCallInvitationListQueriedCallback() {
            @Override
            public void onCallInvitationListQueried(ArrayList<ZIMCallInfo> callList, long nextFlag, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onCallListQueried] size:%s,error cod :%d,error msg:%s", TAG, callList.toString(),errorInfo.code.value(), errorInfo.message);
                if (callback != null) {
                    callback.onCallInvitationListQueried(callList,nextFlag,errorInfo);
                }
            }

        });
    }
    public void callAccept(String callID, ZIMCallAcceptConfig config, ZIMCallAcceptanceSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [callAccept] [zim = null]", TAG);
            return;
        }
        zim.callAccept(callID, config, new ZIMCallAcceptanceSentCallback() {
            @Override
            public void onCallAcceptanceSent(String callID, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onCallAcceptanceSent] callID:%s,error cod :%d,error msg:%s", TAG, callID, errorInfo.code.value(), errorInfo.message);
                if (callback != null) {
                    callback.onCallAcceptanceSent(callID, errorInfo);
                }
            }
        });
    }

    public void callCancel(List<String> invitees, String callID, ZIMCallCancelConfig config, ZIMCallCancelSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [callCancel] [zim = null]", TAG);
            return;
        }

        zim.callCancel(invitees, callID, config, new ZIMCallCancelSentCallback() {
            @Override
            public void onCallCancelSent(String callID, ArrayList<String> errorInvitees, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onCallCancelSent] callID:%s,info error size:%s,error cod :%d,error msg:%s", TAG, callID, errorInvitees.size(), errorInfo.code.value(), errorInfo.message);
                if (callback != null) {
                    callback.onCallCancelSent(callID, errorInvitees, errorInfo);
                }
            }
        });
    }

    public void callReject(String callID, ZIMCallRejectConfig config, ZIMCallRejectionSentCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [callReject] [zim = null]", TAG);
            return;
        }
        zim.callReject(callID, config, new ZIMCallRejectionSentCallback() {
            @Override
            public void onCallRejectionSent(String callID, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onCallRejectionSent] callID:%s,error cod :%d,error msg:%s", TAG, callID, errorInfo.code.value(), errorInfo.message);
                if (callback != null) {
                    callback.onCallRejectionSent(callID, errorInfo);
                }
            }
        });
    }

    public void revokeMessage(ZIMMessage message, ZIMMessageRevokeConfig config, ZIMMessageRevokedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [revokeMessage] [zim = null]", TAG);
            return;
        }
        zim.revokeMessage(message, config, new ZIMMessageRevokedCallback() {
            @Override
            public void onMessageRevoked(ZIMMessage zimMessage, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onMessageRevoked] message id:%s,error cod :%d,error msg:%s", TAG, zimMessage.getMessageID(), errorInfo.code.value(), errorInfo.message);
                if (callback != null) {
                    callback.onMessageRevoked(zimMessage, errorInfo);
                }
            }
        });
    }

    public void queryMessageReceiptsInfo(List<ZIMMessage> messageList, String conversationID, ZIMConversationType conversationType, ZIMMessageReceiptsInfoQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryMessageReceipt] [zim = null]", TAG);
            return;
        }
        zim.queryMessageReceiptsInfo(messageList, conversationID, conversationType, new ZIMMessageReceiptsInfoQueriedCallback() {
            @Override
            public void onMessageReceiptsInfoQueried(ArrayList<ZIMMessageReceiptInfo> infos, ArrayList<Long> errorMessageIDs, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onMessageReceiptsInfoQueried] error cod :%d,error msg:%s", TAG, errorInfo.code.value(), errorInfo.message);
                if (callback != null) {
                    callback.onMessageReceiptsInfoQueried(infos, errorMessageIDs, errorInfo);
                }
            }
        });
    }

    public void queryCombineMessageDetail(ZIMCombineMessage message,ZIMCombineMessageDetailQueriedCallback callback) {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryCombineMessage] [zim = null]", TAG);
            return;
        }
        zim.queryCombineMessageDetail(message, new ZIMCombineMessageDetailQueriedCallback() {
            @Override
            public void onCombineMessageDetailQueried(ZIMCombineMessage message, ZIMError error) {
                AppLogger.getInstance().d("[%s] [onCombineMessageDetailQueried] error cod :%d,error msg:%s,message:%s", TAG, error.code.value(), error.message,message.toString());
                if (callback!=null){
                    callback.onCombineMessageDetailQueried(message,error);
                }
            }

        });
    }
    public void setConversationDraft(String draft, String conversationID, ZIMConversationType conversationType, ZIMConversationDraftSetCallback callback){
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [setConversationDraft] [zim = null]", TAG);
            return;
        }
        zim.setConversationDraft(draft, conversationID, conversationType, new ZIMConversationDraftSetCallback() {
            @Override
            public void onConversationDraftSet(String conversationID, ZIMConversationType conversationType, ZIMError errorInfo) {
                AppLogger.getInstance().d("[%s] [onConversationDraftSet] error cod :%d,error msg:%s", TAG, errorInfo.code.value(), errorInfo.message);
                if (callback!=null){
                    callback.onConversationDraftSet(conversationID,conversationType,errorInfo);
                }
            }
        });
    }
    public void clearAllConversationMessage(){
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [queryCombineMessage] [zim = null]", TAG);
            return;
        }
    }

    // MARK: - Set & Add callback
    public void setRoomMemberJoinCallback(OnRoomMemberJoinCallback callback) {
        memberJoinCallback = callback;
    }

    public void setRoomMemberLeftCallback(OnRoomMemberLeftCallback callback) {
        memberLeftCallback = callback;
    }

    public void addReceivePeerMessageCallback(OnReceivePeerMessage callback) {
        onReceivePeerMessageList.add(callback);
    }

    public void addReceiveGroupMessageCallback(OnReceiveGroupMessage callback) {
        onReceiveGroupMessageList.add(callback);
    }

    public void removeReceivePeerMessageCallback(OnReceivePeerMessage callback) {
        onReceivePeerMessageList.remove(callback);
    }

    public void removeReceiveGroupMessageCallback(OnReceiveGroupMessage callback) {
        onReceiveGroupMessageList.remove(callback);
    }

    public void addReceiveRoomMessageCallback(OnReceiveRoomMessage callback) {
        onReceiveRoomMessageList.add(callback);
    }

    public void removeReceiveRoomMessageCallback(OnReceiveRoomMessage callback) {
        onReceiveRoomMessageList.remove(callback);
    }

    public void addRoomStateChangedCallback(OnRoomStateCallback callback) {
        onRoomStateChangedList.add(callback);
    }

    public void removeRoomStateChangedCallback(OnRoomStateCallback callback) {
        onRoomStateChangedList.remove(callback);
    }

    public void setConversationChangedCallback(OnConversationChangedCallback callback) {
        conversationChangedCallback = callback;
    }

    public void setConversationTotalUnreadMessageCountUpdatedCallback(OnConversationTotalUnreadMessageCountUpdatedCallback callback) {
        conversationTotalUnreadMessageCountUpdatedCallback = callback;
    }

    public void setLoginStateCallback(OnLoginStateCallback callback) {
        loginStateCallback = callback;
    }

    public void setRevokedReceivedCallback(OnRevokedReceivedCallback callback) {
        revokedReceivedCallback = callback;
    }

    public void setMessageSentStatusChangedCallback(OnMessageSentStatusChangedCallback callback) {
        messageSentStatusChangedCallback = callback;
    }

    public void addGroupNameUpdateCallback(OnGroupNameUpdateCallback callback) {
        onGroupNameUpdateCallbacks.add(callback);
    }

    public void removeGroupNameUpdateCallback(OnGroupNameUpdateCallback callback) {
        onGroupNameUpdateCallbacks.remove(callback);
    }

    public void setGroupStateChangedCallback(OnGroupStateChangedCallback callback) {
        groupStateChangedCallback = callback;
    }

    public void setGroupNoticeUpdatedCallback(OnGroupNoticeUpdatedCallback callback) {
        groupNoticeUpdatedCallback = callback;
    }

    public void setGroupAttributesUpdatedCallback(OnGroupAttributesUpdatedCallback callback) {
        groupAttributesUpdatedCallback = callback;
    }

    public void setGroupMemberStateChangedCallback(OnGroupMemberStateChangedCallback callback) {
        groupMemberStateChangedCallback = callback;
    }

    public void setGroupMemberInfoUpdatedCallback(OnGroupMemberInfoUpdatedCallback callback) {
        groupMemberInfoUpdatedCallback = callback;
    }

    public void setRoomOnlineMemberCountCallback(OnRoomOnlineMemberCountCallback callback) {
        roomOnlineMemberCountCallback = callback;
    }


    public void setCallInviteesAnsweredTimeoutCallback(OnCallInviteesAnsweredTimeoutCallback callback) {
        callInviteesAnsweredTimeoutCallback = callback;
    }

    public void setCallInvitationAcceptedCallback(OnCallInvitationAcceptedCallback callback) {
        callInvitationAcceptedCallback = callback;
    }

    public void setCallInvitationCancelledCallback(OnCallInvitationCancelledCallback callback) {
        callInvitationCancelledCallback = callback;
    }

    public void setCallInvitationReceivedCallback(OnCallInvitationReceivedCallback callback) {
        callInvitationReceivedCallback = callback;
    }

    public void setCallInvitationRejectedCallback(OnCallInvitationRejectedCallback callback) {
        callInvitationRejectedCallback = callback;
    }

    public void setCallInvitationTimeoutCallBack(OnCallInvitationTimeoutCallBack callback) {
        callInvitationTimeoutCallBack = callback;
    }

    public void setCallInvitationEndedCallBack(OnCallInvitationEndedCallBack callback) {
        callInvitationEndedCallBack = callback;
    }

    public void setCallUserStateChangedCallBack(OnCallUserStateChangedCallBack callback) {
        callUserStateChangedCallBack = callback;
    }

    public void setFriendEventCallBackCallBack(OnFriendEventCallBack callback) {
        friendEventCallBackCallBack = callback;
    }

    public void setFriendApplicationEvent(OnFriendApplicationEventCallBack callback) {
        friendApplicationEventCallBackCallBack = callback;
    }

    public void setMessageReactionChangedCallback(OnMessageReactionChangedCallback callback) {
        messageReactionChangedCallback = callback;
    }
    public void setMessageReceiptChangedCallback(OnMessageReceiptChangedCallback callback) {
        messageReceiptChangedCallback = callback;
    }

    public void setConversationMessageReceiptChangedCallback(OnConversationMessageReceiptChangedCallback callback) {
        conversationMessageReceiptChangedCallback = callback;
    }

    public void setMessageDeletedCallback(OnMessageDeletedCallback callback){
        messageDeletedCallback = callback;
    }

    public interface OnTokenCallback {
        void OnTokenWillExpire(ZIM zim, int second);
    }

    public interface OnRoomStateCallback {
        void onRoomStateChanged(ZIM zim, ZIMRoomState state, ZIMRoomEvent event, JSONObject extendedData);
    }

    public interface OnLoginStateCallback {
        void onConnectionStateChanged(ZIM zim, ZIMConnectionState state, ZIMConnectionEvent event, JSONObject extendedData);
    }

    public interface OnRevokedReceivedCallback {
        void onMessageRevokeReceived(ZIM zim, ArrayList<ZIMRevokeMessage> messageList);
    }

    public interface OnMessageSentStatusChangedCallback {
        void onMessageSentStatusChanged(ZIM zim, ArrayList<ZIMMessageSentStatusChangeInfo> changedInfos);
    }

    public interface OnRoomMemberJoinCallback {
        void onRoomMemberJoinCallback(ZIM zim, ArrayList<ZIMUserInfo> memberList, final String roomID);
    }

    public interface OnRoomMemberLeftCallback {
        void onRoomMemberLeft(ZIM zim, ArrayList<ZIMUserInfo> memberList, final String roomID);
    }

    public interface OnRoomOnlineMemberCountCallback {
        void onRoomOnlineMemberCount(ZIM zim, int memberCount);
    }

    public interface OnReceivePeerMessage {
        void onReceivePeerMessage(ZIM zim, ArrayList<ZIMMessage> messageList, String fromUserID);
    }

    public interface OnReceiveGroupMessage {
        void onReceiveGroupMessage(ZIM zim, ArrayList<ZIMMessage> messageList, String fromGroupID);
    }

    public interface OnReceiveRoomMessage {
        void onReceiveRoomMessage(ZIM zim, ArrayList<ZIMMessage> messageList, String fromRoomID);
    }

    public interface OnConversationChangedCallback {
        void onConversationChanged(ZIM zim, ArrayList<ZIMConversationChangeInfo> conversationChangeInfoList);
    }

    public interface OnConversationTotalUnreadMessageCountUpdatedCallback {
        void onConversationTotalUnreadMessageCountUpdated(ZIM zim, int totalUnreadMessageCount);
    }

    public interface OnGroupStateChangedCallback {
        void onGroupStateChanged(ZIM zim, ZIMGroupState state, ZIMGroupEvent event, ZIMGroupOperatedInfo operatedInfo, ZIMGroupFullInfo groupInfo);
    }

    public interface OnGroupNameUpdateCallback {
        void onGroupNameUpdated(ZIM zim, String groupName, ZIMGroupOperatedInfo operatedInfo, String groupID);
    }

    public interface OnGroupNoticeUpdatedCallback {
        void onGroupNoticeUpdated(ZIM zim, String groupNotice, ZIMGroupOperatedInfo operatedInfo, String groupID);
    }

    public interface OnGroupAttributesUpdatedCallback {
        void onGroupAttributesUpdated(ZIM zim, ArrayList<ZIMGroupAttributesUpdateInfo> infos, ZIMGroupOperatedInfo operatedInfo, String groupID);
    }

    public interface OnGroupMemberStateChangedCallback {
        void onGroupMemberStateChanged(ZIM zim, ArrayList<ZIMGroupMemberInfo> userList, ZIMGroupMemberState state, ZIMGroupMemberEvent event, ZIMGroupOperatedInfo operatedInfo, String groupID);
    }

    public interface OnGroupMemberInfoUpdatedCallback {
        void onGroupMemberInfoUpdated(ZIM zim, ArrayList<ZIMGroupMemberInfo> userList, ZIMGroupOperatedInfo operatedInfo, String groupID);
    }

    public interface OnCallInvitationTimeoutCallBack {
        void onCallInvitationTimeout(ZIM zim, ZIMCallInvitationTimeoutInfo info,String callID);
    }

    public interface OnCallInvitationAcceptedCallback {
        void onCallInvitationAccepted(ZIM zim, ZIMCallInvitationAcceptedInfo info, String callID);
    }

    public interface OnCallInvitationCancelledCallback {
        void onCallInvitationCancelled(ZIM zim, ZIMCallInvitationCancelledInfo info, String callID);
    }

    public interface OnCallInvitationReceivedCallback {
        void onCallInvitationReceived(ZIM zim, ZIMCallInvitationReceivedInfo info, String callID);
    }

    public interface OnCallInvitationRejectedCallback {
        void onCallInvitationRejected(ZIM zim, ZIMCallInvitationRejectedInfo info, String callID);
    }

    public interface OnCallInviteesAnsweredTimeoutCallback {
        void onCallInviteesAnsweredTimeout(ZIM zim, ArrayList<String> invitees, String callID);
    }

    public interface OnCallInvitationEndedCallBack {
        void onCallInvitationEnded(ZIM zim, ZIMCallInvitationEndedInfo info, String callID);
    }

    public interface OnMessageReactionChangedCallback {
        void onMessageReactionChanged(ZIM zim, ArrayList<ZIMMessageReaction> infos);
    }

    public interface OnCallUserStateChangedCallBack {
        void onCallUserStateChanged(ZIM zim, ZIMCallUserStateChangeInfo info,String callID);
    }

    public interface OnFriendEventCallBack {
        void onFriendInfoUpdated(ZIM zim, ArrayList<ZIMFriendInfo> friendInfoList);
        void onFriendListAdded(ZIM zim, ArrayList<ZIMFriendInfo> friendInfoList);
        void onFriendListDeleted(ZIM zim, ArrayList<ZIMFriendInfo> friendInfoList);
    }

    public interface OnFriendApplicationEventCallBack {
        void
        onFriendApplicationInfoUpdated(ZIM zim,
                                       ArrayList<ZIMFriendApplicationInfo> friendApplicationInfos);
        void
        onFriendApplicationListAdded(ZIM zim,
                                     ArrayList<ZIMFriendApplicationInfo> friendApplicationInfos);
    }

    public interface OnMessageReceiptChangedCallback {
        void onMessageReceiptChanged(ZIM zim, ArrayList<ZIMMessageReceiptInfo> infos);
    }

    public interface OnMessageDeletedCallback {
        void onMessageDeleted(ZIM zim, ZIMMessageDeletedInfo infos);
    }

    public interface OnConversationMessageReceiptChangedCallback {
        void onConversationMessageReceiptChanged(ZIM zim, ArrayList<ZIMMessageReceiptInfo> infos);
    }

    public void destroy() {
        if (zim == null) {
            AppLogger.getInstance().d("[%s] [destroy] [zim = null]", TAG);
            return;
        }
        AppLogger.getInstance().d("[%s] [destroy]", TAG);
        zim.destroy();
        zim = null;
    }


    public void showDebugLog(Activity activity) {
        FloatingView.get().add();
        FloatingView.get().attach(activity);
    }

    public void hideDebugLog(Activity activity) {
        FloatingView.get().detach(activity);

    }

}
